home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Graphics Plus
/
Graphics Plus.iso
/
general
/
raytrace
/
rayshade
/
graphtal.lzh
/
Graphtal.Amiga
/
Z_Buffer.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-08-26
|
8KB
|
334 lines
/*
* Z_Buffer.C - zBuffer implementation for concave polygons.
*
* Copyright (C) 1992, Christoph Streit (streit@iam.unibe.ch)
* University of Berne, Switzerland
* All rights reserved.
*
* This software may be freely copied, modified, and redistributed
* provided that this copyright notice is preserved on all copies.
*
* You may not distribute this software, in whole or in part, as part of
* any commercial product without the express consent of the authors.
*
* There is no warranty or other guarantee of fitness of this software
* for any purpose. It is provided solely "as is".
*
*/
#ifdef AMIGA_GCC
#include "string.h"
#endif
#include <values.h>
#include "Z_Buffer.h"
//___________________________________________________________ EdgeElement
struct EdgeElement {
real x;
real z;
};
declareList(EdgeList, EdgeElement);
implementList(EdgeList, EdgeElement);
//___________________________________________________________ Z_Buffer
Z_Buffer::Z_Buffer(ViewTransform* v, const rcString& msg, const rcString& oname)
: view(v), remark(msg), ymin(resY), ymax(0), numPrimitives(0)
{
resY = view->getResY();
resX = view->getResX();
/*
* Create the yBuckets.
*/
yBuckets = new EdgeList*[resY];
for (register int i=0; i<resY; i++)
yBuckets[i] = new EdgeList(2);
/*
* Create and initialize z-Buffer array.
*/
zBuffer = new float[resY*resX];
for (i=0; i<resY*resX; i++)
zBuffer[i] = MAXFLOAT;
/*
* Create and initialize pixmap.
*/
pixmap = new unsigned char[resY*resX*3];
memset(pixmap, 0, resY*resX*3);
/*
* Attach stream to outfile.
*/
if (oname == "cout")
outfile = stdout;
else
outfile = fopen((const char*) oname, "w");
if (outfile == NULL)
Error(ERR_PANIC, "can't open file " + oname);
}
Z_Buffer::~Z_Buffer()
{
delete view;
delete [] zBuffer;
delete [] pixmap;
for (register int i=0; i<resY; i++)
delete yBuckets[i];
delete [] yBuckets;
}
void Z_Buffer::renderTriangle(const Color& color, const Vector& p1,
const Vector& p2, const Vector& p3)
{
numPrimitives++;
Vector tp1 = view->transformWorld2View(p1);
Vector tp2 = view->transformWorld2View(p2);
Vector tp3 = view->transformWorld2View(p3);
/*
* Front plane clipping (z=0).
*/
if (tp1[2] < 0 || tp2[2] < 0 || tp3[2] < 0)
return;
Vector normal = (tp2-tp1)*(tp3-tp1);
if (!normal.normalize())
return;
Vector lightVector = (tp1+tp2+tp3)/3;
calculateColor(color, normal, lightVector);
tp1 = view->transformView2Screen(tp1);
tp2 = view->transformView2Screen(tp2);
tp3 = view->transformView2Screen(tp3);
addEdge(tp1, tp2);
addEdge(tp2, tp3);
addEdge(tp3, tp1);
render();
}
void Z_Buffer::renderRectangle(const Color& color, const Vector& p1, const Vector& p2,
const Vector& p3, const Vector& p4)
{
numPrimitives++;
Vector tp1 = view->transformWorld2View(p1);
Vector tp2 = view->transformWorld2View(p2);
Vector tp3 = view->transformWorld2View(p3);
Vector tp4 = view->transformWorld2View(p4);
/*
* Front plane clipping (z=0).
*/
if (tp1[2] < 0 || tp2[2] < 0 || tp3[2] < 0 || tp4[2] < 0)
return;
Vector normal = (tp2-tp1)*(tp4-tp1);
if (!normal.normalize())
return;
Vector lightVector = (tp1+tp2+tp3+tp4)/4;
calculateColor(color, normal, lightVector);
tp1 = view->transformView2Screen(tp1);
tp2 = view->transformView2Screen(tp2);
tp3 = view->transformView2Screen(tp3);
tp4 = view->transformView2Screen(tp4);
addEdge(tp1, tp2);
addEdge(tp2, tp3);
addEdge(tp3, tp4);
addEdge(tp4, tp1);
render();
}
void Z_Buffer::renderPolygon(const Color& color, Polygon* p)
{
numPrimitives++;
/*
* Degenerate polygon?
*/
if (p->numVertices() < 3)
return;
/*
* Transform polygon to view space.
*/
p->transform(view->getViewmat());
/*
* Front plane clipping (z=0).
*/
for (register long i=0; i<p->numVertices(); i++)
if (p->vertex(i)[2] < 0)
return;
Vector normal = p->normal();
if (normal.zero())
return;
Vector lightVector;
for (i=0; i<p->numVertices(); i++)
lightVector += p->vertex(i);
lightVector /= p->numVertices();
calculateColor(color, normal, lightVector);
/*
* Add each edge of the polygon in the y-bucket structure.
*/
Vector p1 = view->transformView2Screen(p->vertex(0));
Vector p2;
for (i=1; i<p->numVertices(); i++) {
p2 = view->transformView2Screen(p->vertex(i));
addEdge(p1, p2);
p1 = p2;
}
p2 = view->transformView2Screen(p->vertex(0));
addEdge(p1, p2);
render();
delete p;
}
void Z_Buffer::writePixmap()
{
fprintf(outfile, "P6\n");
fprintf(outfile, "#%s\n", (const char*) remark);
fprintf(outfile, "%d\n%d\n255\n", resX, resY);
fwrite((char*)pixmap, resX*resY*3, 1, outfile);
fclose(outfile);
}
void Z_Buffer::addEdge(const Vector& p1, const Vector& p2)
{
const Vector& start = (p1[1] <= p2[1]) ? p1 : p2;
const Vector& end = (p1[1] <= p2[1]) ? p2 : p1;
int y1 = (int)(start[1]);
int y2 = (int)(end[1]);
/*
* Horizontal edge, do nothing.
*/
if (y1 == y2)
return;
if (y1 < ymin) ymin = y1;
if (y2 > ymax) ymax = y2;
/*
* Set up increments.
*/
real x = start[0];
real z = start[2];
real denom = 1/(end[1]-start[1]);
real dx = (end[0]-x)*denom;
real dz = (end[2]-z)*denom;
EdgeElement edgeElement;
for (register int y=y1; y<y2; y++, x+=dx, z+=dz) {
/*
* No clipping is done, so we have to check for out of screen space.
*/
if (y<0) continue;
if (y>=resY) break;
/*
* Create new EdgeElement entry in the Edgelist sorted by x.
*/
edgeElement.x = x;
edgeElement.z = z;
register long i=0;
while (1) {
if (i>=yBuckets[y]->count()) {
yBuckets[y]->append(edgeElement);
break;
}
if (yBuckets[y]->item(i).x > x) {
yBuckets[y]->insert(i, edgeElement);
break;
}
i++;
}
}
}
void Z_Buffer::render()
{
int x1, x2, offset;
real dx, dz, z;
unsigned char* pixmapPos;
if (ymin < 0) ymin = 0;
if (ymax > resY) ymax = resY;
for (register int y=ymin; y < ymax; y++) {
EdgeList* yBucket = yBuckets[y];
offset = (resY-y)*resY;
for (register long i=0; i < yBucket->count(); i+=2) {
x1 = (int)(yBucket->item(i).x);
x2 = (int)(yBucket->item(i+1).x);
if (x1 != x2) {
dx = yBucket->item(i+1).x - yBucket->item(i).x;
dz = (yBucket->item(i+1).z - yBucket->item(i).z)/dx;
z = yBucket->item(i).z + (yBucket->item(i).x-x1)*dz;
for (register int x=x1; x < x2; x++) {
if (x >= 0 && x < resX) {
pixmapPos = pixmap + 3*(offset+x);
if (z < zBuffer[offset+x]) {
zBuffer[offset+x] = z;
*pixmapPos++ = (unsigned char)(R*255);
*pixmapPos++ = (unsigned char)(G*255);
*pixmapPos = (unsigned char)(B*255);
}
}
z += dz;
}
}
}
yBucket->remove_all();
}
ymin = resY;
ymax = 0;
}
void Z_Buffer::calculateColor(const Color& color, const Vector& normal, const Vector& p)
{
/*
* we don't do back face culling, so take absolute value of dot(light,normal).
* our light sits at (0,0,0) = eye point (=> light vector = (0,0,0) - p)
*/
Vector toEye = -p; toEye.normalize();
float intensity = fabs(toEye^normal);
/*
* we don't calculate the intensity vor each vertex and take an
* average intensity => as a result the color ist usually too dark
* => add an ambient term.
*/
intensity += 0.25; // add an ambient term, because intenisty
if (intensity>1) intensity = 1;
R = color.r()*intensity;
G = color.g()*intensity;
B = color.b()*intensity;
}